package com.gitee.sparta.core.cluster;

import cn.hutool.core.util.StrUtil;
import com.gitee.sparta.core.cluster.balance.LoadBalance;
import com.gitee.sparta.core.cluster.netty.NettyClusterFollower;
import com.gitee.sparta.core.cluster.netty.NettyClusterLeader;
import com.gitee.sparta.core.cluster.nio.NioClusterFollower;
import com.gitee.sparta.core.cluster.nio.NioClusterLeader;
import com.gitee.sparta.core.property.SpartaCoreProperties;
import lombok.Getter;
import lombok.Setter;
import org.redisson.api.RedissonClient;
import org.springframework.context.ApplicationEventPublisher;

/**
 * <p>
 * 集群通讯中传输类型的选择，netty 和 nio
 * </p>
 *
 * @author zyred
 * @since v 0.0.2
 */
public class ClusterTransportFactory {


    /**
     * 构建集群通讯中 leader
     *
     * @param properties     配置文件
     * @param redissonClient redisson client
     * @param loadBalance    负载
     * @return leader
     */
    public static Combination builder(ApplicationEventPublisher publisher,
                                      SpartaCoreProperties properties,
                                      RedissonClient redissonClient,
                                      LoadBalance loadBalance,
                                      ClusterCommonCode ccc) {
        Combination leader;

        String clusterType = properties.getCluster().getClusterTransportType();
        if (StrUtil.equals(ClusterTypeEnum.netty.name(), clusterType)) {
            leader = ClusterTypeEnum.netty.buildType(publisher, properties, redissonClient, loadBalance, ccc);
            ClusterCache.setNetty();
        } else if (StrUtil.equals(ClusterTypeEnum.nio.name(), clusterType)) {
            leader = ClusterTypeEnum.nio.buildType(publisher, properties, redissonClient, loadBalance, ccc);
        } else {
            String error = "[Sparta] Config error properties [spring.sparta.cluster" +
                    ".cluster-transport-type] value error [" + clusterType + "]";
            throw new UnsupportedOperationException(error);
        }
        return leader;
    }


    public enum ClusterTypeEnum implements ClusterType {
        /**
         * netty server
         */
        netty {
            @Override
            public Combination buildType(ApplicationEventPublisher publisher,
                                         SpartaCoreProperties properties,
                                         RedissonClient redissonClient,
                                         LoadBalance loadBalance,
                                         ClusterCommonCode ccc) {
                Combination combination = new Combination();
                NettyClusterLeader leader = new NettyClusterLeader(properties, redissonClient, loadBalance);
                combination.setLeader(leader);
                NettyClusterFollower follower = new NettyClusterFollower(publisher, redissonClient, properties, loadBalance, leader, ccc);
                leader.setFollower(follower);
                combination.setFollower(follower);
                return combination;
            }
        },

        /**
         * nio server
         */
        nio {
            @Override
            public Combination buildType(ApplicationEventPublisher publisher,
                                         SpartaCoreProperties properties,
                                         RedissonClient redissonClient,
                                         LoadBalance loadBalance,
                                         ClusterCommonCode ccc) {
                Combination combination = new Combination();
                NioClusterLeader leader = new NioClusterLeader(properties, redissonClient, loadBalance);
                combination.setLeader(leader);

                combination.setFollower(new NioClusterFollower(publisher, redissonClient,
                        properties, loadBalance, leader, ccc));
                return combination;
            }
        };
    }

    interface ClusterType {

        /**
         * 枚举实现接口
         *
         * @param publisher      事件发布器
         * @param properties     配置文件
         * @param redissonClient redisson client
         * @param loadBalance    负载
         */
        Combination buildType(ApplicationEventPublisher publisher,
                              SpartaCoreProperties properties,
                              RedissonClient redissonClient,
                              LoadBalance loadBalance,
                              ClusterCommonCode ccc);
    }

    @Setter
    @Getter
    public static class Combination {
        AbstractClusterLeader leader;
        AbstractClusterFollower follower;
    }
}
